package pl.piomin.service.blockchain.config;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.Web3jService;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.http.HttpService;
import org.web3j.protocol.ipc.UnixIpcService;
import org.web3j.protocol.ipc.WindowsIpcService;

import java.util.concurrent.TimeUnit;

@Configuration
@ConditionalOnClass(Web3j.class)
@EnableConfigurationProperties(Web3jProperties.class)
public class Web3jAutoConfiguration {

    private static Logger LOG = LoggerFactory.getLogger(Web3jAutoConfiguration.class);

    // 使用@Autowired注解自动注入Web3jProperties对象，该对象包含了与Web3j客户端相关的配置属性
    // 这些配置属性包括但不限于Web3j客户端的连接信息、超时设置等
    // 通过自动注入，可以方便地在类内部访问和使用这些配置属性，从而与以太坊区块链网络进行交互
    @Autowired
    private Web3jProperties properties;

    /**
     * 当Web3j实例不存在时，创建并返回一个新的Web3j实例
     * 此方法用于配置Spring Boot应用程序中的Web3j bean，确保只有在 没有 现有bean时才创建新的实例
     *
     * @return Web3j实例，用于连接和交互以太坊区块链
     */
    @Bean
    @ConditionalOnMissingBean
    public Web3j web3j() {
        // 构建Web3j服务，使用配置的客户端地址
        Web3jService web3jService = buildService(properties.getClientAddress());
        // 日志输出构建的Web3j服务端点地址
        LOG.info("Building service for endpoint: {}", properties.getClientAddress());
        // 使用构建的Web3j服务创建并返回Web3j实例
        return Web3j.build(web3jService);
    }

    @Bean
    // 仅当配置属性满足条件时才创建Bean
    @ConditionalOnProperty(
            prefix = Web3jProperties.WEB3J_PREFIX, // 属性前缀
            name = "admin-client", // 属性名称
            havingValue = "true" // 属性值
    )
    // 创建并配置Admin实例
    public Admin admin() {
        // 根据客户端地址构建Web3j服务实例
        Web3jService web3jService = buildService(properties.getClientAddress());
        // 记录Admin服务构建日志
        LOG.info("Building admin service for endpoint: {}", properties.getClientAddress());
        // 使用Web3j服务实例构建并返回Admin实例
        return Admin.build(web3jService);
    }

    /**
     * 构建Web3j服务实例
     * 本方法根据提供的客户端地址创建 appropriate 类型的Web3j服务实例
     * 如果客户端地址为空或者null，则默认使用HTTP服务
     * 如果地址以"http"开头，则视为完整的HTTP地址，直接用于HTTP服务
     * 如果地址不以"http"开头，则根据操作系统类型选择IPC服务：
     *   - Windows系统使用WindowsIpcService
     *   - 其他系统（例如Linux）使用UnixIpcService
     *
     * @param clientAddress 客户端地址，用于区分和创建不同类型的Web3j服务
     * @return Web3jService 返回对应类型的Web3j服务实例
     */
    private Web3jService buildService(String clientAddress) {
        Web3jService web3jService;

        if (clientAddress == null || clientAddress.equals("")) {
            web3jService = new HttpService(createOkHttpClient());
        } else if (clientAddress.startsWith("http")) {
            web3jService = new HttpService(clientAddress, createOkHttpClient(), false);
        } else if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
            web3jService = new WindowsIpcService(clientAddress);
        } else {
            web3jService = new UnixIpcService(clientAddress);
        }

        return web3jService;
    }

    /**
     * 创建并配置OkHttpClient实例
     * 该方法负责初始化OkHttpClient，并应用必要的配置，包括日志记录和超时设置
     *
     * @return 配置后的OkHttpClient实例
     */
    private OkHttpClient createOkHttpClient() {
        // 创建OkHttpClient.Builder实例，用于构建OkHttpClient
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        // 配置OkHttpClient的日志记录功能
        configureLogging(builder);
        // 配置OkHttpClient的超时设置
        configureTimeouts(builder);
        // 构建并返回配置后的OkHttpClient实例
        return builder.build();
    }

    /**
     * 配置HTTP请求的超时时间
     *
     * @param builder OkHttpClient的构建器，用于配置超时时间
     */
    private void configureTimeouts(OkHttpClient.Builder builder) {
        // 获取HTTP请求的超时时间设置
        Long tos = properties.getHttpTimeoutSeconds();
        if (tos != null) {
            // 配置连接超时时间
            builder.connectTimeout(tos, TimeUnit.SECONDS);
            // 配置读取超时时间，同时设置socket超时时间
            builder.readTimeout(tos, TimeUnit.SECONDS);
            // 配置写入超时时间
            builder.writeTimeout(tos, TimeUnit.SECONDS);
        }
    }

    /**
     * 配置HTTP请求的日志功能
     *
     * @param builder OkHttpClient的构建器，用于添加日志拦截器
     */
    private static void configureLogging(OkHttpClient.Builder builder) {
        // 如果当前环境允许debug级别的日志输出，则配置日志拦截器
        if (LOG.isDebugEnabled()) {
            // 创建一个日志拦截器实例，使用debug级别
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor(LOG::debug);
            // 设置日志拦截器的日志级别为BODY，即会输出请求和响应的完整日志
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            // 将日志拦截器添加到HTTP客户端的拦截器链中
            builder.addInterceptor(logging);
        }
    }


//    @Bean
//    @ConditionalOnBean(Web3j.class)
//    Web3jHealthIndicator web3jHealthIndicator(Web3j web3j) {
//        return new Web3jHealthIndicator(web3j);
//    }
}
